#pragma once

#include <set>
#include <boost/operators.hpp>




namespace boostext
{
	/**
	 * This class is a light wrapper around std::set.
	 * It simply stores a pair, consisting of the value and meta data. However most of
	 * the interface behaves as if this were a simple std::set<value_type>, ie. the presence
	 * of the meta data is not noticed by the user.
	 */
	template
	<
		typename value_type,
		typename meta_type,
		meta_type default_meta_value = meta_type(),
		typename _Pr = std::less<value_type>,
		typename _Alloc = std::allocator<value_type>
	>
	class meta_set
	{
	public:

		/**
		 * This structure is a small wrapper around the given value_type.
		 * It is composed of the actual value and a little flag that tells if the
		 * value is optional or not. How this is interpreted totally depends on the user.
		 */
		struct meta_pair
			: boost::less_than_comparable<meta_pair
			, boost::less_than_comparable<meta_pair, value_type
			, boost::equality_comparable<meta_pair
			, boost::equality_comparable<meta_pair, value_type
			> > > >
		{
			value_type   value;
			meta_type    metaData;

			/**
			 * Create a value_pair for the given value.
			 * The meta data is constructed from the default value, as specified
			 * in the template.
			 */
			meta_pair(const value_type& value)
				: value(value)
				, metaData(default_meta_value)
			{}

			/**
			 * Create a value_pair for the given value and meta data.
			 */
			meta_pair(const value_type& value, const meta_type& metaData)
				: value(value)
				, metaData(metaData)
			{}



			/**
			 * Automatic conversion into the value type.
			 */
			operator const value_type&() const { return value; }

			bool operator==(const value_type& that) const { return value == that; }
			bool operator<(const value_type& that) const { return value < that; }

			bool operator==(const meta_pair& that) const { return value == that.value; }
			bool operator<(const meta_pair& that) const { return value < that.value; }
		};
		///////////////////////////////////////////////////////////////////////////////////////////////

		typedef std::set<meta_pair>                  container;
		typedef typename container::iterator         iterator;
		typedef typename container::const_iterator   const_iterator;
		typedef typename container::size_type        size_type;
		typedef meta_pair                            pair;

	private:

		container   m_data;

	public:

		/**
		 * Create an empty meta_set.
		 */
		meta_set()
		{}



		/**
		 * Insert a value into this set.
		 * The meta data is default constructed.
		 */
		void insert(const value_type& value)
		{
			m_data.insert(value);
		}

		/**
		 * Insert a value and meta data into this set.
		 */
		void insert(const value_type& value, const meta_type& meta)
		{
			m_data.insert(meta_pair(value, meta));
		}

		/**
		 * Find the given value in this set.
		 */
		iterator find(const value_type& value)
		{
			return m_data.find(value);
		}

		/**
		 * Find the given value in this set.
		 */
		const_iterator find(const value_type& value) const
		{
			return m_data.find(value);
		}

		/**
		 * Erase the value pointed to by the given iterator.
		 */
		void erase(iterator iter)
		{
			m_data.erase(iter);
		}

		/**
		 * Test if the given value is in this set.
		 */
		bool contains(const value_type& value) const
		{
			return find(value) != end();
		}



		/**
		 * Get the number of elements in this set.
		 */
		size_type size() const
		{
			return m_data.size();
		}



		/**
		 * Get an iterator to the beginning of this container.
		 */
		iterator begin()
		{
			return m_data.begin();
		}

		/**
		 * Get an iterator to the end of this container.
		 */
		iterator end()
		{
			return m_data.end();
		}

		/**
		 * Get an iterator to the beginning of this container.
		 */
		const_iterator begin() const
		{
			return m_data.begin();
		}

		/**
		 * Get an iterator to the end of this container.
		 */
		const_iterator end() const
		{
			return m_data.end();
		}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
